import type {
  Env,
  InvestMapClass,
  FengMapOpitons,
  GetBoothInfoListParams,
  BoothList,
  FloorList,
  NewInvestMapOption,
  GetLoadMapOptions,
  BoothColorConfig,
} from "#/index.d.ts";
import { getApiUrl, https, random } from "@/utils/common";
import { Axios } from "axios";
import "./libs/fengmap.map-nologo-v3.0.9.min.js";
import "./libs/fengmap.analyser.min.js";
import eventEmit from "@/utils/eventEmit";
import { getBoothInfoList, getMaketInfo, getListBoothColor } from "@/api/index";
import { getBoothColor } from "@/libs/map";
import { isEmpty } from "lodash-es";
import { ErrorCode, getErrorInfo } from "./utils/const.js";

const fengmap = window.fengmap;
class InvestMap implements InvestMapClass {
  https: Axios;
  env: Env;
  floorList: FloorList[]; //楼层列表
  FMap: any;
  on: (string, callback: Function) => void;
  fengMapOptions: FengMapOpitons;
  analyser: any;
  mallCode: string;
  buildingCode: string;
  floorCode: string;
  boothColor: BoothColorConfig;
  loading = false;
  boothList: BoothList[];

  constructor(options: NewInvestMapOption) {
    if (!options.env) {
      eventEmit.emit("error", {
        code: ErrorCode.PARAMS_ERROR,
        message: "缺少必填参数env",
        data: null,
      });
      throw new Error("缺少必填参数env");
    }

    if (!options.el) {
      eventEmit.emit("error", {
        code: ErrorCode.PARAMS_ERROR,
        message: "缺少必填参数el",
        data: null,
      });
      throw new Error("缺少必填参数el");
    }
    if (typeof options.el === "string") {
      options.el = document.getElementById(
        options.el.replace("#", "")
      ) as HTMLElement;
    }
    this.env = options.env;
    this.https = https(options.env, this);
    this.on = eventEmit.on.bind(eventEmit);
    this.fengMapOptions = {
      container: options.el,
      appName: "蜂鸟研发SDK_2_0",
      key: "57c7f309aca507497d028a9c00207cf8",
      mapID: "",
      backgroundColor: options.backgroundColor || "#ffffff",
      viewMode: options.is3D
        ? fengmap.FMViewMode.MODE_3D
        : fengmap.FMViewMode.MODE_2D,
      level: options.level || 1,
      mapZoom: options.mapZoom || 18.2,
      uatEnvironment: options.env === "prod" ? false : true, // false正式环境，true测试环境，默认为false
      themeID: options.themeID || "2001",
      themeURL:
        options.themeURL ||
        getApiUrl(this.env) +
          "/api-urlGisManager-m/mobile/fengmap/api-creator/theme/",
      preLoad: options.preLoad === true ? true : false, // 关闭预加载，解决在iphone系列部分机型上，部分地图切换楼层会出现卡死问题
      highlightColor: options.highlightColor,
      backgroundAlpha: options.backgroundAlpha,
      center: options.center,
      rotation: options.rotation || 0,
      tiltAngle: options.tiltAngle || 30,
      highlightPicker: options.highlightPicker || "click",
    };
    if (options.boothColor) {
      this.boothColor = options.boothColor;
    } else {
      this.boothColor = {};
    }
  }

  async loadMap(options: GetLoadMapOptions) {
    if (this.loading) {
      return;
    }
    const that = this;
    that.loading = true;
    eventEmit.emit("loading", true);
    if (options.mallCode) {
      that.mallCode = options.mallCode;
    }
    if (options.buildingCode) {
      that.buildingCode = options.buildingCode;
    }
    //获取楼层列表
    that.floorList = await that.getFloorList();
    if (!that.floorList) {
      that.loading = false;
      eventEmit.emit("loading", false);
      return;
    }
    if (options.floorCode) {
      let index = this.floorList.findIndex(
        (item) => item.floorCode === options.floorCode
      );
      if (index === -1) {
        that.loading = false;
        eventEmit.emit("loading", false);
        eventEmit.emit("error", {
          code: ErrorCode.FlOOR_NOTFOUND,
          message: getErrorInfo(ErrorCode.FlOOR_NOTFOUND),
          data: null,
        });
        return;
      }
      if (this.floorList[index].mapId === -1) {
        that.loading = false;
        eventEmit.emit("loading", false);
        eventEmit.emit("error", {
          code: ErrorCode.FlOOR_EMPTY,
          message: getErrorInfo(ErrorCode.FlOOR_EMPTY),
          data: null,
        });
        return;
      }
      this.fengMapOptions.mapID = this.floorList[index].buildingId;
      this.floorCode = this.floorList[index].floorCode;
    } else {
      const notEmptyFloorIndex = this.floorList.findIndex(
        (item) => item.mapId !== -1
      );
      if (notEmptyFloorIndex !== -1) {
        this.fengMapOptions.mapID =
          this.floorList[notEmptyFloorIndex].buildingId;
        this.floorCode = this.floorList[notEmptyFloorIndex].floorCode;
      } else {
        this.loading = false;
        eventEmit.emit("loading", false);
        eventEmit.emit("error", {
          code: ErrorCode.BUILD_EMPTY,
          message: getErrorInfo(ErrorCode.BUILD_EMPTY),
          data: null,
        });
        return;
      }
    }

    const floorList: FloorList[] = that.floorList;
    //初始化地图
    await that.initMap();
    const mapFlootList = that.FMap.getFloorInfos();
    that.floorList = floorList.map((item, index) => {
      let floorInfo = mapFlootList.find((i) => i.floorID == item.floorId);
      return {
        floorCode: item.floorCode,
        floorId: item.floorId,
        level: (floorInfo && floorInfo.level) || index,
        mapId: item.mapId,
        floorName: item.floorName,
        buildingId: item.buildingId,
      };
    });
    let currentFloor = that.floorList.find(
      (item) => item.floorCode === that.floorCode
    );
    if (currentFloor && (currentFloor.level || currentFloor.level === 0)) {
      await that.asyncSetLevel(currentFloor.level);
    }
    // 获取展位颜色配置
    if (isEmpty(that.boothColor)) {
      await that.getBoothColor();
    }
    // 渲染展位
    await this.renderBooth();
    return {
      mallCode: this.mallCode,
      floorCode: this.floorCode,
      buildingCode: this.buildingCode,
      floorList: this.floorList,
      FMap: this.FMap,
      boothList: this.boothList,
    };
  }

  initMap() {
    const that = this;
    return new Promise((resolve, rejects) => {
      if (!that.fengMapOptions.mapID) {
        eventEmit.emit("error", {
          code: ErrorCode.PARAMS_ERROR,
          message: "缺少必填参数mapId",
          data: null,
        });
        throw new Error("缺少必填参数mapId");
      }
      if (!that.FMap) {
        that.FMap = new fengmap.FMMap(that.fengMapOptions);
      }
      that.FMap.on("click", (event) => {
        const targets = event.targets[0];
        let isBooth = false;
        let boothInfo = {};
        if (targets) {
          isBooth = true;
          const FID = targets.FID;
          boothInfo = this.boothList.find((item) => item.FID === FID);
        }
        let message = {
          event,
          isBooth,
          boothInfo,
        };
        eventEmit.emit("click", message);
      });

      that.FMap.on("info", (event) => {
        if (
          event.InfoMode &&
          event.InfoMode.code &&
          event.InfoMode.code == 500
        ) {
          that.loading = false;
          eventEmit.emit("loading", false);
          eventEmit.emit("error", {
            code: ErrorCode.HTTP_ERROR,
            message: getErrorInfo(ErrorCode.HTTP_ERROR),
            data: event,
          });
        }
        eventEmit.emit("info", event);
      });
      that.FMap.on("levelChanged", (event) => {
        eventEmit.emit("levelChanged", event);
      });
      that.FMap.on("loaded", (event) => {
        eventEmit.emit("loaded", event);
        resolve(true);
      });
    });
  }

  resetMap() {
    this.mallCode = "";
    this.buildingCode = "";
    this.floorCode = "";
    this.boothList = [];
    this.floorList = [];
    if (this.FMap) {
      this.FMap.dispose();
      this.FMap = null;
    }
    if (this.analyser) {
      this.analyser.dispose();
      this.analyser = null;
    }
  }

  fillShopColor(FidList: BoothList[]) {
    const FMap = this.FMap;
    let fmapNodes: Record<string, any> = {};
    /* 构造初始化查询分析器，在回调中执行 Search 方法 */
    return new Promise((resolve, reject) => {
      this.analyser = new fengmap.FMSearchAnalyser({ map: FMap }, () => {
        let searchRequest = new fengmap.FMSearchRequest();
        searchRequest.levels = [FMap.getLevel()];
        // FMType 对象类型枚举
        // 文档地址：https://developer.fengmap.com/docs/js/v3.0.9/fengmap.FMType.html#.MODEL
        searchRequest.type = fengmap.FMType.MODEL;
        this.analyser.query(searchRequest, (models) => {
          let nodes = FMap.getNodes(models);
          nodes.forEach((item) => {
            fmapNodes[item.FID] = item;
            if (this.boothColor && this.boothColor.publicbooth) {
              item.setColor(this.boothColor.publicbooth);
            }
          });
          FidList.forEach((item) => {
            if (fmapNodes[item.FID]) {
              fmapNodes[item.FID].setColor(item.color, item.opacity || 1);
            }
          });
          resolve(true);
        });
      });
    });
  }

  fillShopText(FidList: BoothList[]) {
    const FMap = this.FMap;
    const that = this;
    let fmapNodes = {};
    return new Promise((resolve, reject) => {
      that.analyser = new fengmap.FMSearchAnalyser({ map: FMap }, function () {
        let searchRequest = new fengmap.FMSearchRequest();
        searchRequest.levels = [FMap.getLevel()];
        searchRequest.type = fengmap.FMType.LABEL;
        that.analyser.query(searchRequest, (models) => {
          let nodes = FMap.getNodes(models);
          nodes.forEach((item) => {
            fmapNodes[item.FID] = item;
          });

          FidList.forEach((item, index) => {
            if (fmapNodes[item.FID]) {
              fmapNodes[item.FID].text = item.text;
            }
          });
          resolve(true);
        });
      });
    });
  }

  setFitView() {
    let floor = this.FMap.getFloor(this.FMap.getLevel());
    let bound = floor.getBound();
    this.FMap.setFitView(bound);
    this.FMap.setRotation({
      rotation: 0,
      animate: true,
      duration: 0.3,
    });
  }

  set3DModel(is3D: boolean) {
    this.FMap.setViewMode({
      mode: is3D ? fengmap.FMViewMode.MODE_3D : fengmap.FMViewMode.MODE_2D, // MODE_2D MODE_3D
      animate: true,
    });
  }

  async changeFloor(floorCode: string) {
    const that = this;
    that.floorCode = floorCode;
    let floorIndex = this.floorList.findIndex(
      (item) => item.floorCode == floorCode
    );
    if (floorIndex != -1) {
      const level = this.floorList[floorIndex].level;
      if (this.floorList[floorIndex].mapId === -1) {
        that.loading = false;
        eventEmit.emit("loading", false);
        eventEmit.emit("error", {
          code: ErrorCode.FlOOR_EMPTY,
          message: getErrorInfo(ErrorCode.FlOOR_EMPTY),
          data: null,
        });
        return;
      }
      await this.asyncSetLevel(level);
      await this.renderBooth();
    } else {
      that.loading = false;
      eventEmit.emit("loading", false);
      eventEmit.emit("error", {
        code: ErrorCode.FlOOR_NOTFOUND,
        message: getErrorInfo(ErrorCode.FlOOR_NOTFOUND),
        data: null,
      });
      return;
    }
    return {
      mallCode: this.mallCode,
      floorCode: this.floorCode,
      buildingCode: this.buildingCode,
      floorList: this.floorList,
      FMap: this.FMap,
      boothList: this.boothList,
    };
  }

  async changeBuliding(opitons: GetLoadMapOptions) {
    if (
      this.loading ||
      (opitons.buildingCode === this.buildingCode &&
        opitons.mallCode === this.mallCode)
    ) {
      return {
        mallCode: this.mallCode,
        floorCode: this.floorCode,
        buildingCode: this.buildingCode,
        floorList: this.floorList,
        FMap: this.FMap,
        boothList: this.boothList,
      };
    }
    this.resetMap();
    let res = await this.loadMap({
      mallCode: opitons.mallCode,
      buildingCode: opitons.buildingCode,
      floorCode: opitons.floorCode,
    });
    return res;
  }

  asyncSetLevel(level) {
    return new Promise((resolve) => {
      this.FMap.setLevel({
        level: level,
        animate: true,
        finish: () => {
          resolve(true);
        },
      });
    });
  }

  async getFloorList(opiton?: GetLoadMapOptions) {
    if (opiton) {
      this.mallCode = opiton.mallCode;
      this.buildingCode = opiton.buildingCode;
    }

    const res = await getMaketInfo(this, {
      mallCode: this.mallCode,
      buildingCode: this.buildingCode,
    });
    if (res.code == 200) {
      const resData = res.data || {};
      const malls = resData.malls || [];
      const index = malls.findIndex((item) => item.mallCode == this.mallCode);
      if (index !== -1) {
        const buildings = malls[index].buildings || [];
        const buildIndex = buildings.findIndex(
          (item) => item.buildingCode === this.buildingCode
        );
        if (buildIndex !== -1) {
          const floorList: FloorList[] = buildings[buildIndex].floors || [];
          floorList.forEach((item) => {
            item.buildingId = buildings[buildIndex].buildingId;
          });
          return floorList;
        } else {
          eventEmit.emit("error", {
            code: ErrorCode.PARAMS_ERROR,
            message: "场馆不存在",
            data: null,
          });
        }
      } else {
        eventEmit.emit("error", {
          code: ErrorCode.PARAMS_ERROR,
          message: "商场信息不存在",
          data: null,
        });
      }
    }
  }

  async getBoothInfoList(option?: GetBoothInfoListParams) {
    let paramOption = {
      mallCode: this.mallCode,
      buildingCode: this.buildingCode,
      floorCode: this.floorCode,
      random: random(),
    };
    if (option) {
      paramOption = {
        ...paramOption,
        ...option,
      };
    }
    const res = await getBoothInfoList(this, paramOption);
    if (res.code == 200) {
      return res.data || [];
    }
  }

  async getBoothColor() {
    const that = this;
    const res = await getListBoothColor(this);
    if (res.code == 200) {
      const list = res.data || [];
      list.forEach((item) => {
        that.boothColor[item.code] = item.fillColor;
      });
    }
  }

  async renderBooth() {
    const that = this;
    // 视图居中
    this.setFitView();
    //获取展位信息
    const boothList = (await that.getBoothInfoList()) as BoothList[];
    //填充展位颜色为主题颜色
    let index = that.floorList.findIndex(
      (item) => item.floorCode === that.floorCode
    );
    if (index !== -1) {
      const boothId = that.floorList[index].mapId;
      boothList.forEach((item) => {
        let boothColor = getBoothColor(item, that.boothColor);
        item.color = boothColor;
        item.text = item.contractBrand;
        item.FID = Number(boothId).toFixed() + "-" + item.geoId;
      });
      await that.fillShopColor(boothList);
      await that.fillShopText(boothList);
    }
    this.boothList = boothList;
    that.loading = false;
    eventEmit.emit("loading", false);
  }
}

export default InvestMap;
